#include <QPainter>
#include <QComboBox>
#include <QTabWidget>
#include <QLayout>
#include <QKeyEvent>
#include <QLineEdit>
#include <QAbstractItemView>
#include <QInputDialog>
#include <QDebug>

#include "QyTabWidget.h"
#include "QyTabNavigator.h"

#define __DBG_LOCATION__ qDebug() << __PRETTY_FUNCTION__ << __LINE__;
#define __DBG_PRINTVALUE( v ) qDebug() << #v << v

QyTabWidget::QyTabWidget( QWidget * parent ) : QTabWidget(parent) {

	p_lastIndex = -1;

	tabLineBox = new QComboBox(this);
	p_tabNavigator = new QyTabNavigator;

	setCornerWidget( tabLineBox, Qt::TopLeftCorner );

	// tabLineBox->setFocusProxy(this);
	// tabBar()->setFocusProxy(this);

	setFocusPolicy(Qt::NoFocus);
	tabLineBox->setFocusPolicy(Qt::NoFocus);
	tabLineBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);

	if ( QLineEdit * lineEdit = tabLineBox->lineEdit() )
		lineEdit->setFocusPolicy(Qt::NoFocus);
	if ( QAbstractItemView * view = tabLineBox->view() )
		view->setFocusPolicy(Qt::NoFocus);
	tabBar()->setFocusPolicy(Qt::NoFocus);

	connect( tabLineBox, SIGNAL( currentIndexChanged(const QString&) ),
		SLOT( setCurrentTabLine(const QString&) ) );
	connect( this, SIGNAL( currentChanged(int) ),
		this, SLOT( currentTabChanged(int) ) );
	connect( p_tabNavigator, SIGNAL( itemChanged(const QString&,int) ),
		this, SLOT( currentTabChanged(const QString&,int) ) );

}

QyTabWidget::~QyTabWidget() {

	delete p_tabNavigator;

}

QList<QWidget*> QyTabWidget::widgetsOnLine( const QString & name ) const {

	return ( tabLines.contains(name) ? tabLines[name]->tabs : QList<QWidget*>() );
}

void QyTabWidget::addTab( const QString & tabLineName, const QString & title, QWidget * widget ) {

	QyTabLine * tabLine = 0;

	if ( tabLines.contains(tabLineName) )
		tabLine = tabLines[tabLineName];

	// qDebug() << "addTab" << title << "on" << tabLineName;

	if (tabLine) {

		tabLine->tabNames.append(title);
		tabLine->tabs.append(widget);

		if ( tabLineName == p_currentTabLine )
			QTabWidget::addTab( widget, title );
		else
			widget->hide();

	} else {

		tabLine = new QyTabLine;

		tabLines[tabLineName] = tabLine;

		tabLine->tabNames.append(title);
		tabLine->tabs.append(widget);
		tabLine->lastIndex = 0;
		tabLineBox->blockSignals(true);
		tabLineBox->addItem(tabLineName);
		tabLineBox->blockSignals(false);

		// qDebug() << "tabLines.count()" << tabLines.count() << tabLines.keys();

		if ( tabLines.count() == 1 )
			setCurrentTabLine(tabLineName);
		else if ( tabLineName == p_currentTabLine )
			QTabWidget::setCurrentWidget(widget);
		else
			widget->hide();

		// resize combobox

	}

	setCurrentWidget(widget);

}

void QyTabWidget::removeTab( QWidget * widget, bool deleteWidget ) {

	if (!widget) {

		qWarning("Can't remove NULL widget from QyTabWidget.");

		return;
	}

	QMapIterator<QString,QyTabLine*> pair(tabLines);
	bool ok = false;

	while ( pair.hasNext() ) {

		pair.next();

		QyTabLine * tabLine = pair.value();
		QString tabLineName = pair.key();
		QList<QWidget*> & widgets = tabLine->tabs;
		int index = 0;

		// qDebug() << "Tab line name" << tabLineName;
		// qDebug() << "Tab names" << tabLine->tabNames;
		// qDebug() << "Tab widgets" << widgets;

		foreach ( QWidget * w, widgets ) {

			if ( w == widget ) {

				ok = true;

				// qDebug() << "remove widget from" << tabLineName;

				widgets.removeAll(widget);
				tabLine->tabNames.removeAt(index);

				blockSignals(true);

				removeTab(index); // widget object is not deleted

				if (deleteWidget)
					widget->deleteLater();

				blockSignals(false);

				if ( widgets.isEmpty() ) {

					delete tabLine;
					tabLines.remove(tabLineName);

					if ( tabLines.count() )
						setCurrentTabLine( tabLines.keys().first() );
					else
						p_currentTabLine = QString();


					int i, count = tabLineBox->count();

					for ( i=0; i<count; i++ ) {

						if ( tabLineBox->itemText(i) == tabLineName ) {

							tabLineBox->removeItem(i);

							break;
						}

					}

				} //else
					// currentTabChanged( currentIndex() );

				break;
			}

			index ++;

		}

	}

	if (!ok)
		qWarning( "Can't remove widget %p from tab lines.", widget );

}

void QyTabWidget::setCurrentTabLine( const QString & tabLineName ) {

	// qDebug() << "setCurrentTabLine";

	QyTabLine * tabLine = 0;

	if ( tabLines.contains(tabLineName) )
		tabLine = tabLines[tabLineName];

	// qDebug() << "tabLine" << tabLine;

	if ( p_currentTabLine == tabLineName || !tabLine )
		return;


	blockSignals(true);

	p_currentTabLine = tabLineName;

	int i, count = tabLineBox->count();
	for ( i=0; i<count; i++ ) {

		if ( p_currentTabLine == tabLineBox->itemText(i) ) {

			tabLineBox->setCurrentIndex(i);

			break;
		}

	}

	QTabWidget::clear();
	i = -1;

	// qDebug() << "tabLine->tabs.count()" << tabLine->tabs.count();

	foreach ( QString tabName, tabLine->tabNames )
		addTab( tabLine->tabs[++i], tabName );

	blockSignals(false);

	if ( tabLine->lastIndex < 0 )
		tabLine->lastIndex = 0;

	bool manualChangeSignal = ( currentIndex() == tabLine->lastIndex );

	setCurrentIndex(tabLine->lastIndex);

	if (manualChangeSignal)
		currentTabChanged(tabLine->lastIndex);

}

void QyTabWidget::setCurrentWidget( QWidget * widget ) {

	QMapIterator<QString,QyTabLine*> pair(tabLines);

	while ( pair.hasNext() ) {

		pair.next();

		QList<QWidget*> & widgets = pair.value()->tabs;
		int index = 0;

		foreach ( QWidget * w, widgets ) {

			if ( w == widget ) {

				if ( pair.key() != p_currentTabLine )
					setCurrentTabLine( pair.key() );

				QTabWidget::setCurrentWidget(widget);

				break;
			}

			index ++;

		}

	}

}

void QyTabWidget::setWidgetTitle( QWidget * widget, const QString & title ) {

	QMapIterator<QString,QyTabLine*> pair(tabLines);

	while ( pair.hasNext() ) {

		pair.next();

		QList<QWidget*> & widgets = pair.value()->tabs;
		int index = 0;

		foreach ( QWidget * w, widgets ) {

			if ( w == widget ) {

				Q_ASSERT( index >= 0 && index < pair.value()->tabNames.count() );

				pair.value()->tabNames[index] = title;

				if ( pair.key() == p_currentTabLine )
					setTabText( index, title );

				QTabWidget::setCurrentWidget(widget);

				break;
			}

			index ++;

		}

	}

}

void QyTabWidget::removeTabLine( QString tabLineName, bool deleteWidget ) {

	if ( tabLineName.isEmpty() ) {

		if ( !tabLines.contains(p_currentTabLine) )
			return;

		tabLineName = p_currentTabLine;

	}

	QyTabLine * tabLine = tabLines[tabLineName];

	if (!tabLine) {

		qWarning( "Detect garbage [%s] (in removeTabLine)", tabLineName.toStdString().c_str() );

		return;
	}

	blockSignals(true);

	// qDebug() << "count before" << QTabWidget::count();

	QTabWidget::clear();

	if (deleteWidget) {

		foreach ( QWidget * w, tabLine->tabs )
			w->deleteLater();

	}

	// qDebug() << "count after" << QTabWidget::count();
	// qDebug() << "index" << index;

	blockSignals(false);

	delete tabLine;
	tabLines.remove(tabLineName);

	if ( tabLines.count() )
		setCurrentTabLine( tabLines.keys().first() );
	else
		p_currentTabLine = QString();

	int i, count = tabLineBox->count();

	for ( i=0; i<count; i++ ) {

		if ( tabLineBox->itemText(i) == tabLineName ) {

			tabLineBox->removeItem(i);

			break;
		}

	}

}

void QyTabWidget::clear( bool deleteWidget ) {

	QMapIterator<QString,QyTabLine*> pair(tabLines);

	blockSignals(true);

	while ( pair.hasNext() ) {

		pair.next();

		if (deleteWidget) {

			QList<QWidget*> & widgets = pair.value()->tabs;

			foreach ( QWidget * widget, widgets )
				widget->deleteLater();

		}

		delete pair.value();

	}

	blockSignals(false);

	p_currentTabLine = QString();

	tabLines.clear();
	tabLineBox->clear();
	QTabWidget::clear();

	// qDebug() << "clear complete";

}

void QyTabWidget::currentTabChanged( int index ) {

	QyTabLine * tabLine = 0;

	if ( tabLines.contains(p_currentTabLine) )
		tabLine = tabLines[p_currentTabLine];

	if (tabLine) {

		if ( index >= 0 && index < tabLine->tabs.count() ) {

			emit tabChanged( p_currentTabLine, currentWidget() );
			tabLine->lastIndex = index;

		} else
			qWarning( "Detected garbage on tabLines (in tabwidget)." );

	}

}

void QyTabWidget::currentTabChanged( const QString & tabLineName, int index ) {

	QyTabLine * tabLine = 0;

	if ( tabLines.contains(tabLineName) )
		tabLine = tabLines[tabLineName];

	p_lastTabLine = p_currentTabLine;
	setCurrentTabLine(tabLineName);

	if (tabLine) {

		if ( index >= 0 && index < tabLine->tabs.count() ) {

			p_lastIndex = tabLine->lastIndex;
			QTabWidget::setCurrentWidget(tabLine->tabs[index]);
			tabLine->lastIndex = index;
			qDebug() << "last next index" << p_lastIndex << index;

		} else
			qWarning( "Detected garbage on tabLines (in navigation)." );

	}

}

void QyTabWidget::keyPressEvent( QKeyEvent * event ) {

	if ( event->key() == Qt::Key_Tab && event->modifiers() == Qt::ControlModifier ) {

		p_tabNavigator->clear();

		QMapIterator<QString,QyTabLine*> pair(tabLines);
		QyTabLine * tabLine = 0;

		while ( pair.hasNext() ) {

			pair.next();
			tabLine = pair.value();
			p_tabNavigator->addSection( pair.key(), tabLine->tabNames, tabLine->lastIndex );

		}

		QRect rootGeo;

		if ( QWidget * w = nativeParentWidget() )
			rootGeo = w->geometry();
		else
			rootGeo = geometry();

		QPoint center( rootGeo.center() );
		int index = ( p_lastIndex < 0 ? ( currentIndex() + 1 ) : p_lastIndex );

		if ( index == currentIndex() )
			index = currentIndex() + 1;

		if ( index >= count() )
			index = 0;

		qDebug() << "p_tabNavigator->setCurrentItem" << (p_lastTabLine.isEmpty() ? p_currentTabLine : p_lastTabLine) << index;

		p_tabNavigator->setCurrentItem( p_lastTabLine.isEmpty() ? p_currentTabLine : p_lastTabLine, index );
		p_tabNavigator->show();
		p_tabNavigator->resize( int( width() * 0.4 ), int( height() * 0.4 ) );
		p_tabNavigator->move( center.x() - p_tabNavigator->width() / 2,
			center.y() - p_tabNavigator->height() / 2 );
		p_tabNavigator->setFocus();
		event->accept();

	} else if ( event->key() == Qt::Key_PageUp && event->modifiers() == Qt::ControlModifier ) {

		int index = currentIndex() - 1;

		if ( index < 0 )
			index = count() - 1;

		p_lastIndex = currentIndex();
		setCurrentIndex(index);

	} else if ( event->key() == Qt::Key_PageDown && event->modifiers() == Qt::ControlModifier ) {

		int index = currentIndex() + 1;

		if ( index >= count() )
			index = 0;

		p_lastIndex = currentIndex();
		setCurrentIndex(index);

	} else if ( event->key() == Qt::Key_PageUp &&
		event->modifiers() == Qt::ControlModifier|Qt::ShiftModifier )
	{

		int index = tabLineBox->currentIndex() - 1;

		if ( index < 0 )
			index = tabLineBox->count() - 1;

		p_lastTabLine = p_currentTabLine;
		tabLineBox->setCurrentIndex(index);

	} else if ( event->key() == Qt::Key_PageDown &&
		event->modifiers() == Qt::ControlModifier|Qt::ShiftModifier )
	{

		int index = tabLineBox->currentIndex() + 1;

		if ( index >= tabLineBox->count() )
			index = 0;

		p_lastTabLine = p_currentTabLine;
		tabLineBox->setCurrentIndex(index);

	} else
		QTabWidget::keyPressEvent(event);

}
